using System;
using System.Collections.Generic;
using SdlDotNet.Core;
using SdlDotNet.Graphics;
using SnakeCharp.Entity;
using System.Drawing;
using SdlDotNet.Input;
using SnakeCharp.UI;
using SnakeCharp.Game;


namespace SnakeCharp
{
    public class Program
    {
        private Snake player;
        private Controller inputController;
        private GameHandler game;
        private GameState gameState;

        //UI components
        private Title titleScreen;
        private Menu menuScreen;

        //GameLoop constants
        private const int TICKS_PER_SECOND = 25;
        private const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
        private const int MAX_FRAMESKIP = 5;
        private UInt32 next_game_tick = 0;
        private int loops;
        private float interpolation;
        
        //Test
        private DebugHud debugHUD;

        /// <summary>
        /// Initializes a new instance of the <see cref="Program"/> class.
        /// This is where the hole application is started.
        /// </summary>
        public Program()
        {
            //Setup private objects
            this.player = new Snake();
            this.debugHUD = new DebugHud();
            this.gameState = GameState.Title; //The correct default value is Title.
            this.game = new GameHandler(this.player);
            this.inputController = new Controller();

            //Setup UI components
            this.titleScreen = new Title();
            this.menuScreen = new Menu();

            //Setup Events
            Events.Quit += new EventHandler<QuitEventArgs>(Events_Quit);
            Events.Tick += new EventHandler<TickEventArgs>(Events_Tick);
            Events.KeyboardDown += new EventHandler<KeyboardEventArgs>(this.inputController.KeyDown);
            Events.KeyboardUp += new EventHandler<KeyboardEventArgs>(this.inputController.KeyUp);
            //Run as fast as possible and control the speed in the GameLoop
            Events.Fps = 1000; 
            
            //Setup Video/Screen
            Screen.Surface = Video.SetVideoMode(Configurations.ScreenWidth, 
                                                Configurations.ScreenHeight, 
                                                Configurations.BitsPerPixel, 
                                                false, 
                                                false, 
                                                false, 
                                                true, 
                                                true);

            Video.WindowCaption = "SnakeCharp - (Alpha) Version 0.1";
            
            SdlDotNet.Input.Mouse.ShowCursor = false;

            //Start the MainGameLoop
            Events.Run();
        }


        #region Events Methods

        /// <summary>
        /// Handles the Tick event of the Events control.
        /// This is the MainGameLoop.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="SdlDotNet.Core.TickEventArgs"/> instance containing the event data.</param>
        protected void Events_Tick(object sender, TickEventArgs e)
        {
            //Clear screen
            Screen.Surface.Fill(Color.Black);

            //Exists if ESC is pressed
            if (inputController.CheckKey(Key.Escape))
                Events.QuitApplication();

            loops = 0;
            while( e.Tick > next_game_tick && loops < MAX_FRAMESKIP) 
            {
                //Update the game
                UpdateGame(e);

                //Check the overall game rules
                CheckRules();

                next_game_tick += SKIP_TICKS;
                loops++;
            }

            interpolation = (float)( e.Tick + SKIP_TICKS - next_game_tick ) / (float)( SKIP_TICKS );
            
            //Render the game
            RenderGame(interpolation);
        }

       

        protected void Events_Quit(object sender, QuitEventArgs e)
        {
            Events.QuitApplication();
        } 

        #endregion

        #region Private Methods

        private void CheckRules()
        {
            //The player has died
            if (this.player.Tail.Count <= 0)
            {
                this.gameState = GameState.Menu;
                
                //ToDo: Make some kind of "NewGame" method
                //and remember du clean up memory
                this.player = new Snake();
                this.game = new GameHandler(this.player);
            }
        }

        private void UpdateGame(TickEventArgs e)
        {
            //Check the state of the game, and update the game accordingly.
            switch (gameState)
            {
                case GameState.Title:
                    this.titleScreen.Update(inputController, ref gameState);
                    break;

                case GameState.Menu:
                    this.menuScreen.Update(inputController, ref gameState);
                    break;

                case GameState.Load:
                    break;

                case GameState.Game:
                    if (this.game != null)
                    {
                        this.game.Update(inputController, e);
                        
                        //Update DebugHud
                        if (Configurations.EnableDebugHUD)
                        {
                            debugHUD.Update(e, player);                            
                        }
                    }
                    break;

                case GameState.Result:
                    break;

                case GameState.Credits:
                    break;

                default:
                    break;
            }
        } 

        private void RenderGame(float interpolation)
        {
            switch (gameState)
            {
                case GameState.Title:
                    Screen.Surface.Blit(this.titleScreen);
                    break;

                case GameState.Menu:
                    Screen.Surface.Blit(this.menuScreen);
                    break;

                case GameState.Load:
                    break;

                case GameState.Game:
                        //Render game
                        this.game.Render(Screen.Surface, interpolation);

                        //Render DebugHud
                        if (Configurations.EnableDebugHUD)
                        {
                            Screen.Surface.Blit(debugHUD.Surface, debugHUD.Rectangle);
                        }
                    break;

                case GameState.Result:
                    break;

                case GameState.Credits:
                    break;

                default:
                    break;
            }

            //NOTE: As the last thing in the MainGameLoop, we should update the Video/Screen.
            //Guess it's importent that we only do a Video.Update() or Video.Screen.Update ONCE!
            //Screen.Surface.Update();
            Video.Update();
        }

       
        #endregion

        #region Main Method
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Program game = new Program();
        } 
        #endregion
    }
}